package rtcsession

import (
	"context"
	"fmt"
	"log"
	"net"
	"time"

	"gitee.com/general252/gortc/pkg/format"
	"gitee.com/general252/gortc/pkg/rtcsdp"
	"gitee.com/general252/gortc/pkg/rtprtcp"
	"gitee.com/general252/gortc/pkg/streammuxer"
	"gitee.com/general252/gortc/pkg/track"
	"github.com/pion/rtcp"
	"github.com/pion/rtp"
)

type ClientSession struct {
	writer track.RtpRtcpWriter

	timeDecoder *rtprtcp.RtpTimeSessionDecoder
	ctx         context.Context
	ctxFunc     context.CancelCauseFunc

	conn *RtcConnection

	offer  *rtcsdp.Session
	answer *rtcsdp.Session

	streamMuxReceive streammuxer.StreamMuxer
	streamMuxSend    streammuxer.StreamMuxer
	receiverTrack    []*track.Receiver
	senderTrack      []*track.Sender

	initialized     bool
	timeFirstPacket time.Time

	pool             *rtprtcp.Pool
	onOrderPacketRTP func(value *OrderPacketRTP)
	onPacketRTP      func(value *OrderPacketRTP)
}

func NewClientSession(writer track.RtpRtcpWriter) *ClientSession {
	ctx, ctxFunc := context.WithCancelCause(context.TODO())
	tis := &ClientSession{
		ctx:              ctx,
		ctxFunc:          ctxFunc,
		writer:           writer,
		timeDecoder:      rtprtcp.NewRtpTimeSessionDecoder(),
		streamMuxSend:    streammuxer.NewStreamMuxer(),
		streamMuxReceive: streammuxer.NewStreamMuxer(),
		pool:             rtprtcp.NewPool(500 * 1024),
	}

	if tis.writer == nil {
		tis.conn = NewRtcConnection(tis)
		tis.writer = tis.conn

	}

	return tis
}

// CreateOffer [1] offer
func (tis *ClientSession) CreateOffer(offer *rtcsdp.Session) {
	tis.offer = offer

	for _, m := range offer.Medias {
		// 本地有发送
		if m.Direction == rtcsdp.DirectionSendOnly || m.Direction == rtcsdp.DirectionSendRecv {
			for _, ct := range m.Formats {

				// 我方发送的流
				streamId := streammuxer.NewStreamIdRTP(m.SSRC, ct.PayloadType())
				_, _ = tis.streamMuxSend.AddStream(streammuxer.StreamInfo{
					Id:          streamId,
					Name:        fmt.Sprintf("sender-%v-%v", m.CName, ct.Codec()),
					OnNewPuller: nil,
					OnDelPuller: nil,
				})
			}
		}
	}
}

// SetRemoteAnswer [2] answer
func (tis *ClientSession) SetRemoteAnswer(answer *rtcsdp.Session) {
	tis.answer = answer

	for _, cm := range answer.Medias {
		// 对方有发送
		if cm.Direction == rtcsdp.DirectionSendOnly || cm.Direction == rtcsdp.DirectionSendRecv {
			for _, ct := range cm.Formats {
				tis._addReceiver(cm, ct)
			}
		}
	}

	for _, cm := range answer.Medias {
		if cm.Direction == rtcsdp.DirectionRecvOnly || cm.Direction == rtcsdp.DirectionSendRecv {
			// 对方接收, 我方发送
			for _, ct := range cm.Formats {
				tis._addSender(cm, ct)
			}
		}
	}
}

type PacketTime struct {
	PTS   time.Duration
	PtsOK bool
	NTP   time.Time
	NtpOK bool
}
type OrderPacketRTP struct {
	PacketTime
	Packet   *rtp.Packet
	Receiver *track.Receiver
}

// OnOrderPacketRTP [3] 接收有序rtp包
func (tis *ClientSession) OnOrderPacketRTP(cb func(value *OrderPacketRTP)) {
	tis.onOrderPacketRTP = cb
}

// OnPacketRTP 接收网络rtp包(udp可能乱序)
func (tis *ClientSession) OnPacketRTP(cb func(value *OrderPacketRTP)) {
	tis.onPacketRTP = cb
}

// Open [4] open
func (tis *ClientSession) Open() error {
	if tis.answer == nil {
		return fmt.Errorf("no answer")
	}

	if tis.conn != nil {
		if err := tis.conn.Dial(tis.answer); err != nil {
			return err
		}
	}

	tis._handleOnPacketRTP()

	for _, t := range tis.receiverTrack {
		_ = t.Open()
	}
	for _, t := range tis.senderTrack {
		_ = t.Open()
	}

	return nil
}

func (tis *ClientSession) Wait() error {
	<-tis.ctx.Done()

	tis.close()
	return context.Cause(tis.ctx)
}

func (tis *ClientSession) close() {
	for _, t := range tis.receiverTrack {
		t.Close()
	}
	for _, t := range tis.senderTrack {
		t.Close()
	}

	if tis.conn != nil {
		tis.conn.Close(fmt.Errorf("end"))
	}
}

func (tis *ClientSession) Close(cause error) {
	tis.ctxFunc(cause)

	log.Printf("close reason: %v", context.Cause(tis.ctx))
}

// ReadPacketRTP [5] 接收到rtp包
func (tis *ClientSession) ReadPacketRTP(pkt *rtp.Packet, addr net.Addr) {
	streamId := streammuxer.NewStreamIdRTP(pkt.SSRC, pkt.PayloadType)
	_ = tis.streamMuxReceive.Write(&streammuxer.StreamData{
		StreamId: streamId,
		Data:     pkt,
		Addr:     addr,
	})
}

// ReadPacketRTCP 接收到rtcp包
func (tis *ClientSession) ReadPacketRTCP(packets []rtcp.Packet, addr net.Addr) {
	for _, pkt := range packets {
		sr, ok := pkt.(*rtcp.SenderReport)
		if !ok {
			continue
		}

		streamId := streammuxer.NewStreamIdRTCP(sr.SSRC)

		_ = tis.streamMuxReceive.Write(&streammuxer.StreamData{
			StreamId: streamId,
			Data:     sr,
			Addr:     addr,
		})
	}
}

// WritePacketRTP [5] 发送rtp包
func (tis *ClientSession) WritePacketRTP(pkt *rtp.Packet, ntp time.Time) error {
	if !tis.initialized {
		tis.initialized = true
		tis.timeFirstPacket = ntp
	}

	streamId := streammuxer.NewStreamIdRTP(pkt.SSRC, pkt.PayloadType)
	_ = tis.streamMuxSend.Write(&streammuxer.StreamData{
		StreamId: streamId,
		Data:     pkt,
		Ntp:      ntp,
		Addr:     nil,
	})

	return fmt.Errorf("not found format by SSRC/PayloadType")
}

// PacketPTS 获取数据包的pts
func (tis *ClientSession) PacketPTS(pkt *rtp.Packet) (time.Duration, bool) {
	if m, ok := tis.findReceiver(pkt.SSRC, pkt.PayloadType); ok {
		return tis.timeDecoder.Decode(m.Format(), pkt)
	}

	return 0, false
}

// PacketNTP 获取数据包的ntp
func (tis *ClientSession) PacketNTP(pkt *rtp.Packet) (time.Time, bool) {
	if m, ok := tis.findReceiver(pkt.SSRC, pkt.PayloadType); ok {
		return m.PacketNTP(pkt)
	}

	return time.Time{}, false
}

// PacketPTSAndNTP 获取数据包的pts/ntp
func (tis *ClientSession) PacketPTSAndNTP(r *track.Receiver, pkt *rtp.Packet) PacketTime {
	var value PacketTime
	value.PTS, value.PtsOK = tis.timeDecoder.Decode(r.Format(), pkt)
	value.NTP, value.NtpOK = r.PacketNTP(pkt)

	return value
}

func (tis *ClientSession) findReceiver(ssrc uint32, pt uint8) (*track.Receiver, bool) {
	for _, m := range tis.receiverTrack {
		if m.Equal(ssrc, pt) {
			return m, true
		}
	}

	return nil, false
}

func (tis *ClientSession) _addReceiver(cm *rtcsdp.Media, ct format.Format) {

	// 我方接收的流
	streamId := streammuxer.NewStreamIdRTP(cm.SSRC, ct.PayloadType())
	_, _ = tis.streamMuxReceive.AddStream(streammuxer.StreamInfo{
		Id:   streamId,
		Name: fmt.Sprintf("reveive-%v-%v", cm.CName, ct.Codec()),
	})

	streamId = streammuxer.NewStreamIdRTCP(cm.SSRC)
	_, _ = tis.streamMuxReceive.AddStream(streammuxer.StreamInfo{
		Id:   streamId,
		Name: fmt.Sprintf("reveive-rtcp-%v-%v", cm.CName, ct.Codec()),
	})

	m := track.NewReceiver(tis.streamMuxReceive, tis.writer, cm, ct)
	tis.receiverTrack = append(tis.receiverTrack, m)
}

func (tis *ClientSession) _addSender(cm *rtcsdp.Media, ct format.Format) {
	m := track.NewSender(tis.streamMuxSend, tis.writer, cm, ct,
		func() (time.Time, bool) {
			return tis.timeFirstPacket, tis.initialized
		},
	)

	tis.senderTrack = append(tis.senderTrack, m)
}

func (tis *ClientSession) GetReceiver() []*track.Receiver {
	return tis.receiverTrack
}

func (tis *ClientSession) GetSender() []*track.Sender {
	return tis.senderTrack
}

// GetReceiveStreamMuxer 流路由, 发送流/接收流
func (tis *ClientSession) GetReceiveStreamMuxer() streammuxer.StreamMuxer {
	return tis.streamMuxReceive
}

// ParsePacket 解析packet
func (tis *ClientSession) ParsePacket(data []byte) ([]rtcp.Packet, *rtp.Packet, error) {
	packetData := tis.pool.Get(len(data))
	copy(packetData, data)

	var objectRtcp rtcp.Header
	if err := objectRtcp.Unmarshal(packetData); err == nil {
		if objectRtcp.Type >= 192 && objectRtcp.Type <= 223 {
			if packets, err := rtcp.Unmarshal(packetData); err != nil {
				return nil, nil, err
			} else {
				return packets, nil, nil
			}
		}
	}

	var objectRtp rtp.Packet
	if err := objectRtp.Unmarshal(packetData); err != nil {
		return nil, nil, err
	} else {
		return nil, &objectRtp, nil
	}
}

func (tis *ClientSession) _handleOnPacketRTP() {
	if tis.onPacketRTP != nil {
		fn := func(receiver *track.Receiver) (string, streammuxer.StreamId) {
			name := fmt.Sprintf("recv-rtp-%v", receiver.Format().Codec())
			streamId := streammuxer.NewStreamIdRTP(receiver.Media().SSRC, receiver.Format().PayloadType())

			return name, streamId
		}
		tis._onPacketRTP(tis.onPacketRTP, fn)
	}

	if tis.onOrderPacketRTP != nil {
		fn := func(receiver *track.Receiver) (string, streammuxer.StreamId) {
			name := fmt.Sprintf("recv-order-rtp-%v", receiver.Format().Codec())
			streamId := streammuxer.NewStreamIdRTPOrder(receiver.Media().SSRC, receiver.Format().PayloadType())

			return name, streamId
		}

		tis._onPacketRTP(tis.onOrderPacketRTP, fn)
	}
}

func (tis *ClientSession) _onPacketRTP(
	cb func(value *OrderPacketRTP),
	getInfo func(receiver *track.Receiver) (string, streammuxer.StreamId),
) {
	for _, v := range tis.GetReceiver() {
		receiver := v
		name, streamId := getInfo(receiver)

		_, _ = tis.GetReceiveStreamMuxer().AddPull(streamId, streammuxer.PullInfo{
			Name: name,
			OnPacket: func(info *streammuxer.StreamInfo, data *streammuxer.StreamData) {
				switch value := data.Data.(type) {
				case *rtp.Packet:
					pkt := value

					if cb != nil {
						cb(&OrderPacketRTP{
							PacketTime: tis.PacketPTSAndNTP(receiver, pkt),
							Packet:     pkt,
							Receiver:   receiver,
						})
					}
				}
			},
		})
	}
}
